home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / MovableModal Library / MovableModalSource.p < prev    next >
Encoding:
Text File  |  1993-07-28  |  10.4 KB  |  416 lines  |  [TEXT/PJMM]

  1. unit MovableModal;
  2.  
  3. { This unit implements a MovableModalDialog routine similar to }
  4. { the Toolbox routine ModalDialog, to be used for movable modal dialogs }
  5.  
  6. { ANTIĀ© 1993 Merzwaren }
  7.  
  8. interface
  9.  
  10.     procedure DisableMenuBar (editMenuID: Integer;
  11.                                     hmnuID: Integer);
  12.     procedure ReEnableMenuBar;
  13.     procedure MovableModalDialog (filterProc: ProcPtr;
  14.                                     var itemHit: Integer);
  15.  
  16. implementation
  17.     uses
  18.         Balloons;
  19.  
  20.     const
  21.  
  22.         kSystemMenuThreshold = -16000;        { menu IDs <= than this are used by the system }
  23.         kMovableModalEventMask = mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + app4Mask;
  24.  
  25.     type
  26.  
  27.         MenuEntry = record
  28.                 hMenu: MenuHandle;
  29.                 leftEdge: Integer;
  30.             end;  {MenuEntry}
  31.  
  32.         MenuList = record
  33.                 offsetToLastMenu: Integer;
  34.                 rightmostEdge: Integer;
  35.                 unused: Integer;
  36.                 theMenus: array[0..0] of MenuEntry;
  37.             end;  {MenuList}
  38.         MenuListPtr = ^MenuList;
  39.         MenuListH = ^MenuListPtr;
  40.  
  41.         MenuBarState = record
  42.                 mbsBarEnable: LongInt;
  43.                 mbsEditEnable: LongInt;
  44.                 mbsEditMenuID: Integer;
  45.                 mbsCutItem: Integer;
  46.                 mbsCopyItem: Integer;
  47.                 mbsPasteItem: Integer;
  48.             end;  {MenuBarState}
  49.         MenuBarStatePtr = ^MenuBarState;
  50.         MenuBarStateH = ^MenuBarStatePtr;
  51.  
  52.     var
  53.  
  54.         saveState: Handle;
  55.  
  56.     function HasHelpManager: Boolean;
  57.         var
  58.             response: LongInt;
  59.     begin
  60.         HasHelpManager := (Gestalt(gestaltHelpMgrAttr, response) = noErr) & BTST(response, gestaltHelpMgrPresent);
  61.     end;  {HasHelpManager}
  62.  
  63.     procedure ForgetHandle (var h: univ Handle);
  64.     begin
  65.         if (h <> nil) then
  66.             begin
  67.                 DisposHandle(h);
  68.                 h := nil;
  69.             end;
  70.     end;  {ForgetHandle}
  71.  
  72.     function GetMenuList: MenuListH;
  73.     inline
  74.         $2EB8, $0A1C;                    { move.l MenuList, (sp) }
  75.  
  76.     function GetMenuItemFromKeyEquiv (theMenu: MenuHandle;
  77.                                     keyEquiv: Char): Integer;
  78.         var
  79.             item, nItems: Integer;
  80.             cmd: Char;
  81.     begin
  82.         GetMenuItemFromKeyEquiv := 0;
  83.         nItems := CountMItems(theMenu);
  84.         for item := 1 to nItems do
  85.             begin
  86.                 GetItemCmd(theMenu, item, cmd);
  87.                 if (cmd = keyEquiv) then
  88.                     begin
  89.                         GetMenuItemFromKeyEquiv := item;
  90.                         Exit(GetMenuItemFromKeyEquiv);
  91.                     end;
  92.             end;  { for }
  93.     end;  {GetMenuItemFromKeyEquiv}
  94.  
  95.     procedure DisableMenuBar (editMenuID: Integer;
  96.                                     hmnuID: Integer);
  97.         var
  98.             menuList: MenuListH;
  99.             i, nMenus: Integer;
  100.             theMenu: MenuHandle;
  101.             menuID: Integer;
  102.             menuEnable, barEnable: LongInt;
  103.             theDialog: DialogPtr;
  104.             hasBalloons, needEditMenu: Boolean;
  105.             err: OSErr;
  106.     begin
  107.  
  108. { determine if the Help manager is available }
  109.         hasBalloons := HasHelpManager;
  110.  
  111. { determine if the frontmost dialog contains edit fields }
  112.         theDialog := FrontWindow;
  113.         needEditMenu := (theDialog <> nil) & (DialogPeek(theDialog)^.editField >= 0);
  114.  
  115. { get a handle to the menu list and count the menus }
  116.         menuList := GetMenuList;
  117.         nMenus := menuList^^.offsetToLastMenu div SizeOf(MenuEntry);
  118.  
  119. { create a parameter block for saving menu bar state information }
  120.         saveState := NewHandleClear(SizeOf(MenuBarState));
  121.         HLock(saveState);
  122.  
  123.         with MenuBarStateH(saveState)^^ do
  124.             begin
  125.  
  126.                 barEnable := 0;
  127.  
  128. { walk the menu list }
  129.                 for i := 0 to nMenus - 1 do
  130.                     begin
  131.  
  132. { get menu handle, menu ID and enable flags for this menu }
  133.                         theMenu := menuList^^.theMenus[i].hMenu;
  134.                         menuID := theMenu^^.menuID;
  135.                         menuEnable := theMenu^^.enableFlags;
  136.  
  137. { do nothing if this is a system menu }
  138.                         if (menuID <= kSystemMenuThreshold) then
  139.                             Cycle;
  140.  
  141. { if this is the Edit menu and we need it, do some special processing }
  142.                         if ((needEditMenu) and (menuID = editMenuID)) then
  143.                             begin
  144.  
  145. { save edit menu ID }
  146.                                 mbsEditMenuID := editMenuID;
  147.  
  148. { save the enable flags for later restoration }
  149.                                 mbsEditEnable := menuEnable;
  150.  
  151. { find which items are Cut, Copy and Paste }
  152.                                 mbsCutItem := GetMenuItemFromKeyEquiv(theMenu, 'X');
  153.                                 mbsCopyItem := GetMenuItemFromKeyEquiv(theMenu, 'C');
  154.                                 mbsPasteItem := GetMenuItemFromKeyEquiv(theMenu, 'V');
  155.  
  156. { enable Cut, Copy and Paste }
  157.                                 menuEnable := 1 + BSL(1, mbsCutItem) + BSL(1, mbsCopyItem) + BSL(1, mbsPasteItem);
  158.                                 theMenu^^.enableFlags := menuEnable;
  159.  
  160.                                 Cycle;
  161.                             end;  { if menuID = editMenuID }
  162.  
  163. { if this menu is enabled, disable it and set the corresponding bit in barEnable }
  164.                         if (BTST(menuEnable, 0)) then
  165.                             begin
  166.                                 barEnable := BOR(barEnable, BSL(1, i));
  167.                                 DisableItem(theMenu, 0);
  168.                             end;
  169.  
  170. { remap the help strings for this menu }
  171.                         if (hasBalloons) then
  172.                             err := HMSetMenuResID(menuID, hmnuID);
  173.  
  174.                     end;  { for }
  175.  
  176.                 mbsBarEnable := barEnable;
  177.  
  178.             end;  { with }
  179.  
  180. { unhighlight the highlighted menu (if any) and redraw the menu bar }
  181.         HiliteMenu(0);
  182.         DrawMenuBar;
  183.  
  184. { unlock state info parameter block }
  185.         HUnlock(saveState);
  186.  
  187.     end;  {DisableMenuBar}
  188.  
  189.     procedure ReEnableMenuBar;
  190.         var
  191.             menuList: MenuListH;
  192.             i, nMenus: Integer;
  193.             theMenu: MenuHandle;
  194.             menuID: Integer;
  195.             hasBalloons: Boolean;
  196.             err: OSErr;
  197.     begin
  198.  
  199. { sanity check: make sure saveState isn't NIL }
  200.         if (saveState = nil) then
  201.             Exit(ReEnableMenuBar);
  202.  
  203. { determine if the Help manager is available }
  204.         hasBalloons := HasHelpManager;
  205.  
  206. { get a handle to the menu list and count the menus }
  207.         menuList := GetMenuList;
  208.         nMenus := menuList^^.offsetToLastMenu div SizeOf(MenuEntry);
  209.  
  210.         HLock(saveState);
  211.  
  212.         with MenuBarStateH(saveState)^^ do
  213.             begin
  214.  
  215. { walk the menu list }
  216.                 for i := 0 to nMenus - 1 do
  217.                     begin
  218.  
  219. { get menu handle and menu ID for this menu }
  220.                         theMenu := menuList^^.theMenus[i].hMenu;
  221.                         menuID := theMenu^^.menuID;
  222.  
  223. { do nothing if this is a system menu }
  224.                         if (menuID <= kSystemMenuThreshold) then
  225.                             Cycle;
  226.  
  227. { restore old enable state for this menu }
  228.                         if (menuID = mbsEditMenuID) then
  229.                             theMenu^^.enableFlags := mbsEditEnable
  230.                         else if (BTST(mbsBarEnable, i)) then
  231.                             EnableItem(theMenu, 0);
  232.  
  233. { unmap the help strings for this menu }
  234.                         if (hasBalloons) then
  235.                             err := HMSetMenuResID(menuID, -1);
  236.  
  237.                     end;  { for }
  238.  
  239.             end;  { with }
  240.  
  241. { forget about the menu bar parameter block }
  242.         HUnlock(saveState);
  243.         ForgetHandle(saveState);
  244.  
  245. { redraw the menu bar }
  246.         DrawMenuBar;
  247.  
  248.     end;  {ReEnableMenuBar}
  249.  
  250.     function CallFilter (dialog: DialogPtr;
  251.                                     var event: EventRecord;
  252.                                     var item: Integer;
  253.                                     filterProc: ProcPtr): Boolean;
  254.     inline
  255.         $205F,        {    movea.l    (sp)+, a0    }
  256.         $4E90;        {    jsr            (a0)        }
  257.  
  258.     function GetDABeeper: ProcPtr;
  259.     inline
  260.         $2EB8, $0A9C;                    { move.l DABeeper, (sp) }
  261.  
  262.     procedure CallBeeper (soundNo: Integer;
  263.                                     beeperProc: ProcPtr);
  264.     inline
  265.         $205F,        {    movea.l    (sp)+, a0    }
  266.         $4E90;        {    jsr            (a0)        }
  267.  
  268.     function DoMenuChoice (theDialog: DialogPtr;
  269.                                     var theEvent: EventRecord;
  270.                                     var itemHit: Integer;
  271.                                     menuChoice: LongInt): Boolean;
  272.         var
  273.             menuID, menuItem: Integer;
  274.             currentEditField, itemType: Integer;
  275.             itemHandle: Handle;
  276.             itemRect: Rect;
  277.             err: OSErr;
  278.     begin
  279.         DoMenuChoice := false;
  280.  
  281.         if (saveState = nil) then
  282.             Exit(DoMenuChoice);
  283.  
  284.         menuID := HiWord(menuChoice);
  285.         menuItem := LoWord(menuChoice);
  286.  
  287.         HLock(saveState);
  288.         with MenuBarStateH(saveState)^^ do
  289.             begin
  290.  
  291.                 if (menuID = mbsEditMenuID) then
  292.                     begin
  293.  
  294. { find the current edit field }
  295.                         currentEditField := DialogPeek(theDialog)^.editField + 1;
  296.                         GetDItem(theDialog, currentEditField, itemType, itemHandle, itemRect);
  297.  
  298. { if the current edit field is an enabled item, exit from MovableModalDialog loop }
  299.                         if (BAND(itemType, itemDisable) = 0) then
  300.                             begin
  301.                                 DoMenuChoice := true;
  302.                                 itemHit := currentEditField;
  303.                             end;
  304.  
  305. { perform edit operation }
  306.                         if (menuItem = mbsCutItem) then
  307.                             begin
  308.                                 DlgCut(theDialog);
  309.                                 err := ZeroScrap;
  310.                                 err := TEToScrap;
  311.                             end
  312.                         else if (menuItem = mbsCopyItem) then
  313.                             begin
  314.                                 DlgCopy(theDialog);
  315.                                 err := ZeroScrap;
  316.                                 err := TEToScrap;
  317.                             end
  318.                         else if (menuItem = mbsPasteItem) then
  319.                             DlgPaste(theDialog);
  320.                     end;
  321.  
  322.             end;  { with }
  323.  
  324.         HUnlock(saveState);
  325.         HiliteMenu(0);
  326.     end;  {DoMenuChoice}
  327.  
  328.     function HandleMouseDown (theDialog: DialogPtr;
  329.                                     var theEvent: EventRecord;
  330.                                     var itemHit: Integer): Boolean;
  331.         var
  332.             partCode: Integer;
  333.             wind: WindowPtr;
  334.             beeper: ProcPtr;
  335.             dragRect: Rect;
  336.     begin
  337.         HandleMouseDown := false;
  338.  
  339. { find out where the click went down in }
  340.         partCode := FindWindow(theEvent.where, wind);
  341.  
  342. { if the click went in the menu bar, just call MenuSelect }
  343.         if (partCode = inMenuBar) then
  344.             begin
  345.                 HandleMouseDown := DoMenuChoice(theDialog, theEvent, itemHit, MenuSelect(theEvent.where));
  346.                 Exit(HandleMouseDown);
  347.             end;
  348.  
  349. { if the user clicked somewhere outside the dialog, call the beeper }
  350.         if (not PtInRgn(theEvent.where, WindowPeek(theDialog)^.strucRgn)) then
  351.             begin
  352.                 beeper := GetDABeeper;
  353.                 if (beeper <> nil) then
  354.                     CallBeeper(1, beeper);
  355.                 Exit(HandleMouseDown);
  356.             end;
  357.  
  358. { now, we have to handle the only thing DialogSelect doesn't do for us: dragging }
  359.         if (partCode = inDrag) & (theDialog = wind) then
  360.             begin
  361.                 dragRect := GetGrayRgn^^.rgnBBox;
  362.                 DragWindow(wind, theEvent.where, dragRect);
  363.                 theEvent.what := nullEvent;
  364.             end;
  365.  
  366.     end;  {HandleMouseDown}
  367.  
  368.     procedure MovableModalDialog (filterProc: ProcPtr;
  369.                                     var itemHit: Integer);
  370.         var
  371.             savePort: GrafPtr;
  372.             theDialog: DialogPtr;
  373.             theEvent: EventRecord;
  374.             gotEvent: Boolean;
  375.     begin
  376.         itemHit := 0;
  377.  
  378. { get a pointer to the frontmost window (which should be our movable dialog) }
  379.         theDialog := FrontWindow;
  380.         if (theDialog = nil) then
  381.             Exit(MovableModalDialog);
  382.  
  383. { set thePort to the dialog }
  384.         GetPort(savePort);
  385.         SetPort(theDialog);
  386.  
  387. { modal dialog event loop }
  388.         repeat
  389.  
  390. { yield time to other processes and retrieve next event from the queue }
  391.             gotEvent := WaitNextEvent(kMovableModalEventMask, theEvent, 0, nil);
  392.  
  393. { the filter proc is the first one to get a chance to process the event }
  394.             if (filterProc <> nil) & CallFilter(theDialog, theEvent, itemHit, filterProc) then
  395.                 Leave;
  396.  
  397. { then comes our own processing of clicks in the menu bar and in the drag bar }
  398.             if (theEvent.what = mouseDown) & HandleMouseDown(theDialog, theEvent, itemHit) then
  399.                 Leave;
  400.  
  401. { and our processing of keyboard equivalents for the Edit items }
  402.             if (theEvent.what = keyDown) & (BAND(theEvent.modifiers, cmdKey) <> 0) & DoMenuChoice(theDialog, theEvent, itemHit, MenuKey(CHR(BAND(theEvent.message, charCodeMask)))) then
  403.                 Leave;
  404.  
  405. { finally we let the Toolbox do its own thing }
  406.             if IsDialogEvent(theEvent) & DialogSelect(theEvent, theDialog, itemHit) then
  407.                 Leave;
  408.  
  409.         until false;
  410.  
  411. { restore the old port }
  412.         SetPort(savePort);
  413.  
  414.     end;  {MovableModalDialog}
  415.  
  416. end.